﻿using Inaction.Criteria;
using Inaction.Mapping;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Inaction.Linq.Clause
{
    class MethodCallClause : WhereClause
    {
        MethodInfo method = null;

        List<WhereClause> Arguments = new List<WhereClause>();

        List<ParaValue> paraValues = new List<ParaValue>();

        public MethodCallClause(LinqRoot root, MethodCallExpression e)
            : base(root, e)
        {
            method = e.Method;
            var obj = e.Object;

            if (obj == null)
            {
                if (method.Name == "Any")
                {
                    Operate = Criteria.CriteriaOperate.Exists;
                    Left = LinqParse.Parse(e.Arguments[0], root);
                    Right = LinqParse.Parse(e.Arguments[1], root);
                }
                else if (method.Name == "Contains")
                {
                    Operate = CriteriaOperate.In;
                    if(method.DeclaringType == typeof(System.Linq.Enumerable))
                    {
                        Left = LinqParse.Parse(e.Arguments[0],root);
                        Right = LinqParse.Parse(e.Arguments[1],root);
                    }
                    else  if(method.DeclaringType.GetInterface(typeof(System.Collections.IEnumerable).FullName) != null)
                    {
                        Left = LinqParse.Parse(e.Arguments[0],root);
                        Right = LinqParse.Parse(e.Arguments[1],root);
                    }
                }
                return;
            }
            else
            {
                if (method.Name == "Contains")
                {
                    Operate = CriteriaOperate.In;
                    if (method.DeclaringType == typeof(System.Linq.Enumerable)
                        ||method.DeclaringType.GetInterface(typeof(System.Collections.IEnumerable).FullName) != null)
                    {
                        Left = LinqParse.Parse(e.Arguments[0], root);
                        var value = (obj as System.Linq.Expressions.MemberExpression).GetValue() as IEnumerable;
                        var enumerator = (value as IEnumerable).GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            Arguments.Add(new ConstantClause(root, enumerator.Current));
                        }
                    }
                    return;
                }
            }

            #region Cintains=>In()

            #endregion

            if (obj is Expression)
            {
                Left = LinqParse.Parse(obj as Expression, root);
            }

            Type = obj.Type;

            foreach (var a in e.Arguments)
            {
                Arguments.Add(LinqParse.Parse(a, root));
            }

            if (Type == typeof(string))
            {
                var list = new List<string>();
                string formatStr = string.Empty;
                if (method.Name == "StartsWith")
                    formatStr = "{0}%";
                else if (method.Name == "EndsWith")
                    formatStr = "%{0}";
                else if (method.Name == "Contains")
                    formatStr = "%{0}%";
                foreach (var a in Arguments)
                {
                    if (a is ValueClause)
                    {
                        var val = a as ValueClause;
                        val.ParaValue.Value = string.Format(formatStr, val.ParaValue.Value);
                    }
                    else if( a is MemberClause)
                    {
                        var val = a as MemberClause;
                        if (val.Right == null && val.Left.IsValue)
                        {
                            var o= val.Left as ValueClause;
                            o.ParaValue.Value = string.Format(formatStr, o.ParaValue.Value);
                        }
                    }
                }
                Operate = CriteriaOperate.Like;
            }
        }
        public override string GetClauseSql()
        {
            if (Operate == Criteria.CriteriaOperate.Exists)
            {
                return Left.GetClauseSql() + GetSql();
            }
            var s = Rule.EnumDescription.GetDescription(Operate);
            if (Arguments.Count > 0)
            {
                return Left.GetClauseSql() + s + "(" + string.Join(",", Arguments.Select(p => p.GetClauseSql())) + ")";
            }
            else
            {
                if (Right is MemberClause)
                {
                    var leftSql= Left.GetClauseSql();
                    var rightSql = Right.GetClauseSql();
                    return rightSql + s + (Operate == CriteriaOperate.In ? string.Format("({0})", leftSql) : leftSql);
                }
                else
                {
                    return Left.GetClauseSql() + s + ParaValue.Name;
                }
            }
        }

        private string GetSql()
        {
            if (Operate != Criteria.CriteriaOperate.Exists)
            {
                return null;
            }
            var s = Rule.EnumDescription.GetDescription(Operate);
            var subClass = ClassInfo.LoadInfo(Right.Root.RootType);
            var formatTableName = Right.Root.FormatTableName;
            string sql = s + "(Select * from " + subClass.FormatTableName + " As " + formatTableName;

            var classInfo = ClassInfo.LoadInfo(Left.Root.RootType);

            var forgignKey = subClass.Table.ForeignKeys.FirstOrDefault(p => p.ReferenceTable.Name.Equals(classInfo.Table.Name, StringComparison.OrdinalIgnoreCase));
            sql += " Where " + formatTableName + "." + forgignKey.Column.FormatName + " = " + classInfo.FormatTableName + "." + classInfo.PrimaryKey.Column.Name + " ";
            if (!string.IsNullOrEmpty(Right.Root.WhereSql))
            {
                sql += Rule.EnumDescription.GetDescription(CriteriaLogic.And) + Right.Root.WhereSql;
            }
            return sql + ")";
        }
    }
}
